<?php
/**
 * helper.func.php
 * @copyright (c) 2021-2030, Hangzhou Smplote Tech Co., Ltd.
 * This is NOT a freeware, use is subject to license terms.
 * @package helper.function.php
 * @link https://www.smplote.com/
 * @author smplote@gmail.com
 * @$Id: helper.func.php 310000 2020-11-07 21:13:44 $
 * */

use App\Consts\CacheKeyConst;
use App\Consts\GlobalConst;
use App\Repository\Repository;
use App\Services\Service;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Response;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Symfony\Component\HttpKernel\Exception\HttpException;

if (!function_exists('service')) {
    /**
     * get service instance
     * @return \App\Services\Service|array|string|null
     */
    function service()
    {
        static $service = [];
        if ($service instanceof Service) {
            return $service;
        }
        return new Service();
    }
}

if (!function_exists('repository')) {
    /**
     * get repository instance
     * @return \App\Repository\Repository|array|string|null
     */
    function repository()
    {
        static $repository = [];
        if ($repository instanceof Repository) {
            return $repository;
        }
        return new Repository();
    }
}

if (! function_exists('routes')) {
    /**
     * Generate the URL to a named routes.
     *
     * @param string $name
     * @param array $parameters
     * @param string $domain
     * @return string
     */
    function routes($name, $parameters = [], $domain = '')
    {
        $domain = $domain ?: config('app.url');
        if (('http' === substr($name, 0 ,4)) && false === strpos($name, $domain)) {
            $str = str_replace('://', '::', $name);
            $str = explode('/', $str, 2);
            return $domain . '/' . (!empty($str[1]) ? $str[1] : '');
        }
        return $domain . app('url')->route($name, $parameters, false);
    }
}

if (!function_exists('isLocal')) {
    /**
     * 判断当前环境是否为 local
     * @return bool
     */
    function isLocal()
    {
        return env('APP_ENV') === 'local';
    }
}

if (!function_exists('isDev')) {
    /**
     * 判断当前环境是否为 dev
     * @return bool
     */
    function isDev()
    {
        return env('APP_ENV') === 'dev';
    }
}

if (!function_exists('isTest')) {
    /**
     * 判断当前环境是否为 test
     * @return bool
     */
    function isTest()
    {
        return env('APP_ENV') === 'test';
    }
}

if (!function_exists('isProd')) {
    /**
     * 判断当前环境是否为 prod
     * @return bool
     */
    function isProd()
    {
        return env('APP_ENV') === 'prod';
    }
}

if (!function_exists('isDebug')) {
    /**
     * 判断是否开启 debug
     * @return bool
     */
    function isDebug()
    {
        return env('APP_DEBUG') !== false;
    }
}

if (!function_exists('isAccess')) {
    /**
     * 判断是否开启 access
     * @return bool
     */
    function isAccess()
    {
        return env('API_ACCESS') !== false;
    }
}

if (!function_exists('getRealIp')) {
    /**
     * 获取客户端真实IP
     * @return string|null
     */
    function getRealIp()
    {
        $ip = '';
        $server = $_SERVER;
        if (!empty($server['HTTP_X_FORWARDED_FOR'])) {
            $ips = explode(',', $server['HTTP_X_FORWARDED_FOR']);
            $ip = $ips[0];
        } elseif (!empty($server['REMOTE_ADDR'])) {
            $ip = $server['REMOTE_ADDR'];
        }
        return $ip ?: request()->getClientIp();
    }
}

if (!function_exists('lang')) {
    /**
     * 获取语言包翻译
     * [!] 语言包配置文件 ：resource/lang/*
     * @param $key
     * @return array|\Illuminate\Contracts\Translation\Translator|string|null
     */
    function lang($key)
    {
        if (false !== strpos($key, '.')) {
            if (false === strpos($key, ' ')) {
                return trans($key);
            } else {
                $model = explode('.', $key, 2);
                if (isset($model[1]) && $model[1]) {
                    return trans($model[0] . '.' . $model[1]);
                }
            }
        }
        if (app('translator')->has('default.' . $key)) {
            return trans('default.' . $key);
        }
        return $key;
    }
}

if (!function_exists('corsHeader')) {
    /**
     * 获取 Cors Headers
     * [!] 可参见 config/cors.php 详细配置
     * @return array
     */
    function corsHeader()
    {
        $origin = request()->headers->get('Origin') ?: '';
        $headers = 'X-Requested-With, Authorization, Origin, Content-Type, Cookie, Set-Cookie, X-CSRF-TOKEN, Accept, Authorization, X-XSRF-TOKEN, Token';
        return [
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Allow-Origin' => $origin,
            'Access-Control-Allow-Methods' => 'GET, POST, PATCH, PUT, OPTIONS',
            'Access-Control-Allow-Headers' => $headers,
        ];
    }
}

if (!function_exists('jsonReturn')) {
    /**
     * 接口统一 json 返回
     * [!] 重定向支持，append：
     *     - $data['__REDIRECT_URL'] = 'https://www.baidu.com';
     * [!] Cookie支持，append：
     *     - $data['__COOKIES'] = ['token' => 'XXXX', 'uid' => '00XP'];
     * @param array $data
     * @param int $code
     * @param string $message
     * @param bool $isBuild
     * @return JsonResponse|\Illuminate\Http\RedirectResponse|array
     */
    function jsonReturn($data = [], $code = \App\Consts\ExceptionCodeConst::API_SUCCESS, $message = '', $isBuild = false)
    {
        $code = intval($code);
        $success = $code == \App\Consts\ExceptionCodeConst::API_SUCCESS ? true : false;
        if (!$success) {
            $message = $message ? : lang('Failed');
        } else {
            $message = $message ? : lang('Success');
        }
        // 判断是否可以直接返回
        $keys = ['status', 'code', 'msg', 'result'];
        $keys = array_merge($keys, [ 'total', 'rows']);
        if (is_array($data)) {
            if (!empty($data) && empty(array_diff(array_keys($data), $keys))) {
                return response()->json($data);
            } elseif (empty($data)) {
                $data = (object)[];
            }
        } elseif ($data instanceof JsonResponse) {
            $data = $data->getOriginalContent();
            if (!empty($data) && empty(array_diff(array_keys($data), $keys))) {
                return response()->json($data);
            }
        } elseif ($data instanceof Response) {
            $content = $data->getContent();
            $data = json_decode($content, true);
            if (!empty($data) && empty(array_diff(array_keys($data), $keys))) {
                return response()->json($data);
            }
            $data = $content;
        } elseif ($data instanceof Collection) {
            $data = $data->toArray();
        } elseif (is_object($data)) {
            $data = (array)$data ?: (json_decode(json_encode($data), true) ?: json_encode($data));
        } elseif (empty($data)) {
            //$data 绝对等于 false 时，result 返回false
            $data = $data === false ? false : (object)[];
        }
        $format = [
            'status' => $success ? "success" : "failed",
            'code' => $code,
            'msg' => $message,
            'result' => $data,
        ];
        // 重定向 和 Cookie 支持
        if (is_array($data) && isset($data['__REDIRECT_URL'])) {
            return redirect()->away($data['__REDIRECT_URL']);
        } elseif (is_array($data) && isset($data['__COOKIES'])) {
            $cookie = $data['__COOKIES'];
            unset($format['result']['__COOKIES']);
            if (empty($format['result'])) $format['result'] = (object)[];
            $response = response()->json($format);
            if (is_array($cookie)) {
                foreach ($cookie as $key => $val) {
                    !is_numeric($key) && $response = $response->cookie($key, $val);
                }
            } elseif (is_string($cookie)) {
                $response = $response->cookie('__COOKIES', $cookie);
            }
            return $response;
        }
        if ($isBuild) {
            return $format;
        }
        return response()->json($format);
    }
}

if (!function_exists('sendSentry')) {
    /**
     * send exception to sentry
     * @param $exception
     * @return bool
     */
    function sendSentry($exception)
    {
        if (app()->bound('sentry')) {
            return app('sentry')->captureException($exception);
        }
        return false;
    }
}

if (!function_exists('appThrow')) {
    /**
     * app error throw
     * @param \Exception|mixed $exception
     * @param bool $extra
     * @param bool $isSend
     */
    function appThrow($exception, $extra = true, $isSend = false)
    {
        // send sentry log
        if ($isSend) sendSentry($exception);
        $message = $exception->getMessage();
        $extra && $message .= GlobalConst::ERROR_FILE_FLAG . ' : ' . $exception->getFile() . ' - ' . $exception->getLine();
        throw new HttpException($exception->getCode() ? : \App\Consts\ExceptionCodeConst::CODE_FAIL, $message);
    }
}

if (!function_exists('apiShutdown')) {
    /**
     * api shutdown
     * @param $message
     * @param int $code
     */
    function apiShutdown($message, $code = \App\Consts\ExceptionCodeConst::CODE_FAIL)
    {
        throw new HttpException($code, "# [{$code}] - " . $message);
    }
}

if ( ! function_exists('getVal') )
{
    /**
     * Function get value from data
     *
     * @param array $data
     * @param string $key
     * @param string $default
     * @param null $callback
     * @return mixed|string
     */
    function getVal($data, $key, $default = '', $callback = null) {
        if (false !== strpos($key, '.')) {
            // only two level
            list($key1, $key2) = explode('.', $key, 2);
            if (isset($data[$key1][$key2])) {
                if (is_callable($callback)) {
                    return $callback($data[$key1][$key2]) ? : $default;
                }
                return $data[$key1][$key2] ? : $default;
            }
            return $default;
        }
        if (isset($data[$key])) {
            if (is_callable($callback)) {
                return $callback($data[$key]) ? : $default;
            }
            return $data[$key] ? : $default;
        }
        return $default;
    }
}

if (!function_exists('enableQueryLog')) {
    /**
     * enable query log
     * @return bool
     */
    function enableQueryLog()
    {
        DB::connection()->enableQueryLog();
        return true;
    }
}

if (!function_exists('getQueryLog')) {
    /**
     * get query log
     * @param bool $dump
     * @return mixed
     */
    function getQueryLog($dump = false)
    {
        $logs = DB::connection()->getQueryLog();
        $dump && dd($logs);
        return $logs;
    }
}

if (!function_exists('getSql')) {
    /**
     * 根据 query 获取 sql 语句
     * @param \Illuminate\Database\Eloquent\Builder|string $query
     * @param array $bindings
     * @param bool $die
     * @return mixed|string
     */
    function getSql($query, $bindings = [], $die = false)
    {
        $sql = is_string($query) ? $query : $query->toSql();
        $bindings = $bindings ?: $query->getBindings();
        $sql = str_replace('?', "'%s'", $sql);
        $sql = vsprintf($sql, $bindings);
        if ($die) dd($sql);
        return $sql;
    }
}

if (!function_exists('getCacheKey')) {
    /**
     * get cache key
     * @param $key
     * @param array $data
     * @return string
     */
    function getCacheKey($key, $data = [])
    {
        if (empty($data)) {
            return $key;
        }
        return vsprintf($key, $data);
    }
}

if (!function_exists('getCacheByRetry')) {
    /**
     * get cache by retry
     * @param $key
     * @param string $driver
     * @return mixed|null
     */
    function getCacheByRetry($key, $driver = 'redis')
    {
        static $retry = 0;
        try {
            $val = Cache::store($driver)->get($key);
        } catch (\Exception $e) {
            sleep(1);
            if ($retry >= 5) {
                return null;
            }
            $retry++;
            return getCacheByRetry($key, $driver);
        }
        return $val;
    }
}

if (!function_exists('sendSubMailMsg')) {
    /**
     * 发送 SubMail 短信通知
     * @param string $mobile
     * @param string $ProID
     * @param array $varList
     * @example varList : ["code" => 1234]
     * @return mixed
     */
    function sendSubMailMsg($mobile, $ProID, $varList)
    {
        require_once(app_path() . '/Common/Src/submail/app_config.php');
        require_once(app_path() . '/Common/Src/submail/SUBMAILAutoload.php');
        // 先从缓存里读取配置
        $key = getCacheKey(CacheKeyConst::SUBMAIL_CONFIGURE);
        $config = Cache::get($key);
        if (empty($config)) {
            // 读不到缓存从文件中读取
            $config = !empty($message_configs) ? $message_configs : [];
            // 文件中仍然读不到，从 env 中获取
            if (empty($config)) {
                $config = [
                    'appid' => env('SUBMAIL_APP_ID', ''),
                    'appkey' => env('SUBMAIL_APP_KEY', ''),
                    'sign_type' => env('SUBMAIL_SIGN_TYPE', 'md5'),
                ];
            }
            // 配置存入缓存，防止多线程消费找不到全局配置
            Cache::put($key, $config);
        }
        $subMail = new MESSAGEXsend($config);
        $subMail->setTo($mobile);
        $subMail->SetProject($ProID);
        foreach ($varList as $key => $val) {
            $subMail->AddVar($key, $val);
        }
        return $subMail->xsend();
    }
}

if (!function_exists('getConfigByKey')) {
    /**
     * 从 msg_config 表中获取指定配置
     * @param $key
     * @return bool
     */
    function getConfigByKey($key)
    {
        return \repository()->MsgConfigRepository->getConfigByKey($key);
    }
}



